/*
 * Created on Feb 16, 2004
 *
 * Author: Sandeep Prakash, modified by Boon Siew, LTM, Klarinda
 * Project: Sucxentv2 (using leaf nodes only)
 */
package dbsucxentW.storageW.parserW;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

// makes use of the xml.sax to parse the xml document using default handler
/** This class is used to parse the XML document by extending SAXParser
 *  this class is inherited by BranchDataSummarizer & BranchOrderDecorator
 *  There are 2 round of parsing:
 * 		1. to get branch order and RValue	(BranchOrderDecorator)
 * 		2. to get DeweyOrderSum & SiblingSum	(BranchDataSummarizer)
 */
public abstract class NodeExtractor extends DefaultHandler {

    protected int _order = 0;			//leafOrder
    protected int _depth = 0;
    protected StringBuilder _path;
    protected StringBuilder _leafValue;
    //extend BS
    
    // <editor-fold desc="Element Names">
    /**
     * _elementName[i] stores the name of the ancestor at level i of the current element's path expression
     */
    private String[] elementNames;

    public String[] getElementNames() {
        return this.elementNames;
    }
    
    public String getElementNameAtLevel(int level) {
        return this.elementNames[level];
    }
    // </editor-fold>

    protected boolean started = false;	// added by ltm to solve insert leaf nodes with blank values as well
    protected final static int START_ELEMENT = 0;
    protected final static int END_ELEMENT = 1;
    protected int _state = -1;
    protected int _branchOrder = 0;

    public NodeExtractor() {
        _leafValue = new StringBuilder();
        _path = new StringBuilder();
        elementNames = new String[255];
    }

    /**
     * Receive notification of the beginning of the document.
     * @see org.xml.sax.ContentHandler#startDocument()
     */
    @Override
    public void startDocument() throws SAXException {
    }

    /**
     * Receive notification of the start of an element.
     * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
     */
    @Override
    public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
        if (_state != START_ELEMENT) {
            _state = START_ELEMENT;
            _branchOrder = _depth; //Assign BranchOrder to the depth of this element's parent
        }

        started = true;

        //to handle mixed content before the start tag, ex: <a>a1<b>...
        if (_leafValue.toString().trim().length() > 0) {
            leaf();
            _branchOrder = _depth; //Assign BranchOrder to the depth of this element's parent
            _leafValue = new StringBuilder();
        }
        _path.append(".");
        _path.append(localName);
        _path.append("#");
        _depth++;

        elementNames[_depth] = localName;
        processAttributes(attributes);
    }

    /**
     * Receive notification of the end of an element.
     * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
     */
    @Override
    public void endElement(String uri, String localName, String qName) throws SAXException {
        //Call leaf() only if leaf was encountered
        if (_leafValue.toString().trim().length() > 0 || started) {
            //to handle mixed content between 2 end tags ex: ...</b>a3</a> -> update the branchOrder
            if (_state == END_ELEMENT) {
                _branchOrder = _depth;
            }
            leaf();
        }
        started = false;
        elementNames[_depth] = null; //erase the element names that have been closed; added to handle mixed-content
        _depth--;

        clearPath();
        _leafValue = new StringBuilder();
        _state = END_ELEMENT;
    }

    /**
     * Receive notification of the end of the document
     * @throws SAXException
     */
    @Override
    public void endDocument() throws SAXException {
    }

    //Note that	the element node is the parent of each of these attribute nodes; however, an attribute
    //node is not a child of the element node. Therefore attribute should not be included in calculating RValue
    protected void processAttributes(Attributes attributes) {
        int attrCount = attributes.getLength();

        for (int i = 0; i < attrCount; i++) {
            _path.append(".@");
            _path.append(attributes.getQName(i));
            _path.append("#");
            _leafValue = new StringBuilder(attributes.getValue(i));

            _depth++;

            elementNames[_depth] = attributes.getQName(i);

            leafAttribute();	//klarinda 20060831

            _depth--;

            clearPath();
            _leafValue = new StringBuilder();
        }
    }

    /**
     * Receive notification of character data inside an element.
     * @see org.xml.sax.ContentHandler#characters(char[], int, int)
     */
    @Override
    public void characters(char[] ch, int start, int length) throws SAXException {
        //Ignore all chunks that do not start with a proper character
        if (length > 0) {
            _leafValue.append(ch, start, length);
        }
    }

    /**
     * Remove the last element in path
     */
    private void clearPath() {
        int index = _path.lastIndexOf(".");
        _path.delete(index, Integer.MAX_VALUE);
    }

    /**
     * This method is called whenever a leaf node : attribute or element is seen.
     * These methods will be overridden in the subclasses
     */
    protected abstract void leaf();
    protected abstract void leafAttribute();
}
